"""基础视图类"""
import datetime
import json
from datetime import date
from io import BytesIO
from urllib.parse import quote

import numpy as np
import pandas as pd
from flask import make_response

from air_web.web_flask.common.constants import ReturnType, Status, StatusMsg
from air_web.web_flask.common.tools import get_default_result


class BasePage:
    module = "Base"

    @classmethod
    def get_page_name(cls):
        return cls.__name__

    @classmethod
    def get_model_name(cls):
        return cls.module

    @staticmethod
    def return_common_func(
        result, scroll=False, custom_response=False, paging=False
    ):
        """视图函数返回通用处理"""
        response = get_default_result()

        if custom_response:
            response["status"] = result["status"]
            response["msg"] = result["msg"]
            return json.dumps(response)

        # 新增分页参数处理，total以及返回的result，在函数中提前处理好，直接返回result赋值即可
        if paging:
            response = result
            return json.dumps(response)

        if result == ReturnType.ERROR:
            response["status"] = Status.SERVER_ERROR
            response["msg"] = StatusMsg.SERVER_ERROR
        elif result == ReturnType.EMPTY:
            response["status"] = Status.EMPTY_DATA_ERROR
            response["msg"] = StatusMsg.EMPTY_DATA_ERROR
        else:
            if result != ReturnType.OK:
                if scroll:
                    response["data"]["total"] = result[-1][0]
                    response["data"]["pageSize"] = result[-1][1]
                    response["data"]["pageNum"] = result[-1][2]
                    response["data"]["result"] = result[:-1]
                else:
                    response["data"]["result"] = result
        return json.dumps(response, cls=NpEncoder)

    @staticmethod
    def output_xlsx(writer: pd.ExcelWriter, sheet_name: str, df: pd.DataFrame):
        """
        对将要生成的xlsx文件做规范化输出
        Args:
            writer:  pd.ExcelWriter
            sheet_name: str sheet name
            df: pd.DataFrame

        Returns: writer

        """
        workbook = writer.book
        worksheet = writer.sheets[sheet_name]
        # 表头格式
        header_format = workbook.add_format(
            {
                "bold": True,
                "align": "center",
                "fg_color": "#D7E4BC",
                "border": 1,
            }
        )

        for col_num, value in enumerate(df.columns.values):
            # 设置自动列宽
            worksheet.set_column(
                col_num, col_num, len(str(value).encode("utf-8")) * 2 / 3 + 4
            )
            # 启用以上表头格式
            worksheet.write(0, col_num, value, header_format)

        # worksheet.data_validation("N2:N{}".format(df.shape[0] + 1),
        #                           {"validate": "list", "source": constants.HB_CHECK_TYPE_ENUMERATE})
        # 设置图表保护---不允许修改
        # worksheet.protect(constants.XLSX_PROTECT_PWD, {'format_columns': True})
        # 指定部分单元格允许修改
        # worksheet.unprotect_range("M2:O{}".format(df.shape[0] + 1), "myrange")

        return writer

    @staticmethod
    def return_excel_file(
        data_df: pd.DataFrame, sheet_name: str, file_name: str
    ):
        """
        根据DataFrame创建Excel返回实例
        Args:
            data_df: pd.DataFrame 要导出的数据
            sheet_name: str sheet name
            file_name: str file name

        Returns: flask Response instance

        """
        x_io = BytesIO()
        writer = pd.ExcelWriter(
            x_io, engine="xlsxwriter", date_format="yyyy/mm/dd"
        )

        data_df.to_excel(
            writer,
            sheet_name=sheet_name,
            index=False,
            header=False,
            startrow=1,
        )
        writer = BasePage.output_xlsx(writer, sheet_name, data_df)
        writer.save()

        dt = datetime.datetime.now()
        resp = make_response(x_io.getvalue())
        file_name = quote(file_name)
        resp.headers[
            "Content-Disposition"
        ] = "attachment; filename={}-{}.xlsx".format(
            file_name, dt.strftime("%Y-%m-%d")
        )
        resp.headers[
            "Content-Type"
        ] = "application/octet-stream; charset=utf-8"

        return resp

    @staticmethod
    def return_excel_file_from_df_list(
        data_df_list: list, sheet_name_list: list, file_name: str
    ):
        """
        根据DataFrame创建Excel返回实例
        Args:
            data_df_list: pd.DataFrame 要导出的数据
            sheet_name_list: str sheet name
            file_name: str file name

        Returns: flask Response instance

        """
        x_io = BytesIO()
        writer = pd.ExcelWriter(
            x_io, engine="xlsxwriter", date_format="yyyy/mm/dd"
        )
        for data_df, sheet_name in zip(data_df_list, sheet_name_list):
            data_df.to_excel(
                writer,
                sheet_name=sheet_name,
                index=False,
                header=False,
                startrow=1,
            )
            writer = BasePage.output_xlsx(writer, sheet_name, data_df)
        writer.save()

        dt = datetime.datetime.now()
        resp = make_response(x_io.getvalue())
        file_name = quote(file_name)
        resp.headers[
            "Content-Disposition"
        ] = "attachment; filename={}-{}.xlsx".format(
            file_name, dt.strftime("%Y-%m-%d")
        )
        resp.headers[
            "Content-Type"
        ] = "application/octet-stream; charset=utf-8"

        return resp

    @staticmethod
    def get_return_excel_file_from_df(
        data_df,
        columns_chinese_dict,
        sheet_name,
        file_name,
        round_two_decimal_column=None,
        percent_column=None,
    ):
        """
        根据DataFrame 获取输出的标准response实例
        Args:
            data_df: pd.DataFrame 数据
            columns_chinese_dict: dict  key is english column name. value is chinese name
            round_two_decimal_column: list
            percent_column: list
            sheet_name: str sheet name
            file_name: str file name

        Returns: flask response instance

        """
        if isinstance(data_df, int):
            return BasePage.return_common_func(ReturnType.ERROR)
        if not data_df.empty:
            list_data = data_df[columns_chinese_dict.keys()]
            if round_two_decimal_column:
                list_data[round_two_decimal_column] = list_data[
                    round_two_decimal_column
                ].round(2)
            if percent_column:
                list_data[percent_column] = (
                    list_data[percent_column] * 100
                ).round(2)
            list_data.rename(columns=columns_chinese_dict, inplace=True)
        else:
            list_data = pd.DataFrame(
                columns=list(columns_chinese_dict.values())
            )
        list_data = list_data.mask(list_data.isnull(), None)
        return BasePage.return_excel_file(
            list_data, sheet_name=sheet_name, file_name=file_name
        )

    @staticmethod
    def get_is_download(validation_data: dict) -> tuple:
        """
        获取是否下载标记
        Args:
            validation_data: dict 验证通过的数据

        Returns: tuple  (bool, dict) dict is updated

        """
        is_download = False
        if "download" in validation_data:  # 获取是否下载
            is_download = (
                validation_data.get("download", "false").lower() == "true"
            )
            del validation_data["download"]  # 将该字段刨除，避免干扰SQL语句
        return is_download, validation_data


class NpEncoder(json.JSONEncoder):
    """自定义json序列化类"""

    def default(self, obj):
        if isinstance(obj, np.integer):
            return int(obj)
        if isinstance(obj, np.floating):
            return float(obj)
        if isinstance(obj, np.ndarray):
            return obj.tolist()
        if isinstance(obj, pd._libs.tslibs.nattype.NaTType):
            return None
        if isinstance(obj, date):
            return obj.strftime("%Y-%m-%d")
        return super().default(obj)
